package cn.dansj.controller;

import cn.dansj.bean.CurrentThreadLocalUserInfo;
import cn.dansj.bean.NexusRepository;
import cn.dansj.bean.NexusRepositoryAsset;
import cn.dansj.bean.NexusRepositoryComponent;
import cn.dansj.common.redis.annotation.cache.UseCache;
import cn.dansj.common.request.annotation.http.GetMethod;
import cn.dansj.common.request.annotation.http.PostMethod;
import cn.dansj.common.request.annotation.http.RequestMappingMethod;
import cn.dansj.common.request.annotation.http.RequestParam;
import cn.dansj.common.utils.bean.Return;
import cn.dansj.common.utils.bean.UserPwd;
import cn.dansj.common.utils.enums.BooleanType;
import cn.dansj.common.utils.json.DictMap;
import cn.dansj.common.request.RequestUtils;
import cn.dansj.common.request.RestTemplateUtils;
import cn.dansj.common.utils.http.UrlUtils;
import cn.dansj.common.utils.transfer.Transformation;
import cn.dansj.common.utils.transfer.Verification;
import cn.dansj.enums.GlobalEnv;
import cn.dansj.utils.project.ProjectUtils;
import jakarta.servlet.http.HttpServletResponse;
import org.jetbrains.annotations.NotNull;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.*;
import org.springframework.util.StreamUtils;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.client.HttpServerErrorException;
import org.springframework.web.client.RequestCallback;
import org.springframework.web.client.ResponseExtractor;

import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;

@RequestMappingMethod(value = "/mvn")
public class NexusController {
    private static final RestTemplateUtils restTemplateUtils = RestTemplateUtils.defaultInstance();
    public static final String nexusUrl = GlobalEnv.NexusHost + "/service/rest";
    private static final String adminUserLabel = "user_admin";
    private static final String deployUserLabel = "user_deploy";

    private UserPwd getUserInfo(String userLabel) {
        final DictMap nexus = ProjectUtils.getSettingByRedis("nexus");
        final String[] user = nexus.getString(userLabel).split(" ");
        return new UserPwd(user[0], user[1]);
    }

    public String getNexusRequestUrl(String url) {
        return nexusUrl + url;
    }

    private HttpHeaders getRequestHeaders() {
        final HttpHeaders headers = new HttpHeaders();
        final boolean islogin = CurrentThreadLocalUserInfo.islogin();
        final UserPwd userInfo = getUserInfo(islogin ? adminUserLabel : deployUserLabel);
        headers.setBasicAuth(userInfo.getUsername(), userInfo.getPassword());
        headers.set("user-agent", "apache-maven/console");
        RequestUtils.getRequest().setAttribute("fromRestTemplate", true);
        return headers;
    }

    /**
     * T是SpEL中的类型操作符，用于指定一个类。T(类名)表示引用指定的类
     */
    @GetMethod(value = "/repositories", permission = BooleanType.TRUE)
    @UseCache(name = "${#method}.islogin:${T(cn.dansj.bean.CurrentThreadLocalUserInfo).islogin()}")
    public Return<?> repositories() {
        final List<NexusRepository> response = restTemplateUtils.get(getNexusRequestUrl("/v1/repositorySettings"), new ParameterizedTypeReference<>() {
        }, getRequestHeaders());
        return Return.T(response);
    }

    @GetMethod(value = "/repository/list", permission = BooleanType.TRUE)
    public Return<?> repositoryDetail(@RequestParam DictMap map) {
        final boolean match = map.values().stream().allMatch(Verification::checkNull);
        if (match) return Return.T(false, "输入必要的参数进行搜索");
        final String params = UrlUtils.joinParams(map);
        return Return.T(restTemplateUtils.get(getNexusRequestUrl("/v1/search" + params), DictMap.class, getRequestHeaders()).getList("items", NexusRepositoryComponent.class));
    }

    @PostMethod("/component/remove/{id}")
    public Return<?> removeComponent(@RequestParam(path = true) String id) {
        return Return.T(restTemplateUtils.delete(getNexusRequestUrl("/v1/components/" + id), String.class, getRequestHeaders()));
    }

    @PostMethod("/asset/remove/{id}")
    public Return<?> removeAsset(@RequestParam(path = true) String id) {
        return Return.T(restTemplateUtils.delete(getNexusRequestUrl("/v1/assets/" + id), String.class, getRequestHeaders()));
    }

    @ExceptionHandler
    public Return<?> exception(HttpServerErrorException httpServerErrorException) {
        return Return.T(false, Transformation.patternGet(httpServerErrorException.getMessage(), "(.+):(.+)", 2).trim().replace("\"", "")).setResponseCode(500);
    }

    @GetMethod(value = "/assets/download/{sha1}", permission = BooleanType.TRUE)
    public void downloadAsset(@RequestParam(path = true) String sha1, HttpServletResponse response) {
        final HttpHeaders requestHeaders = getRequestHeaders();
        final List<NexusRepositoryAsset> assets = restTemplateUtils.get(getNexusRequestUrl("/v1/search/assets?sha1=" + sha1), DictMap.class, requestHeaders).getList("items", NexusRepositoryAsset.class);
        if (Verification.checkNull(assets)) {
            RequestUtils.response404();
            return;
        }

        RequestCallback requestCallback = request -> {
            request.getHeaders().setAccept(Arrays.asList(MediaType.APPLICATION_OCTET_STREAM, MediaType.ALL));
            request.getHeaders().addAll(requestHeaders);
        };

        restTemplateUtils.getRestTemplate().execute(getNexusRequestUrl("/v1/search/assets/download?sha1=" + sha1), HttpMethod.GET, requestCallback, getResponseExtractor(response, assets));
    }

    @NotNull
    private static ResponseExtractor<Boolean> getResponseExtractor(HttpServletResponse response, List<NexusRepositoryAsset> assets) {
        final NexusRepositoryAsset nexusRepositoryAsset = assets.get(0);

        return clientHttpResponse -> {
            final HttpHeaders headers = clientHttpResponse.getHeaders();
            headers.forEach((key, value) -> response.setHeader(key, value.get(0)));
            response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + URLEncoder.encode(nexusRepositoryAsset.getName(), StandardCharsets.UTF_8));
            StreamUtils.copy(clientHttpResponse.getBody(), response.getOutputStream());
            return true;
        };
    }
}
